﻿using System;
using System.Reflection;
using Tinter.Application.Modules.Finance.DTOs;
using Tinter.Application.Resources;
using Tinter.Common.Logging;
using Tinter.Common.Pagination;
using Tinter.Common.TypeMappers;
using Tinter.Models;
using Tinter.Models.Modules.Finance;

namespace Tinter.Application.Modules.Finance.Services
{
    /// <summary>
    /// The finance management service implementation.
    /// <see cref="Tinter.Application.Modules.Finance.Services.IFinanceAppService"/>
    /// </summary>
    public class FinanceAppService : IFinanceAppService
    {
        #region Fields

        private IExpenseProjectRepository expenseProjectRepository;
        private ITypeMapper typeMapper;
        private ILogger logger;

        #endregion

        #region Constructos

        public FinanceAppService(IExpenseProjectRepository expenseProjectRepository, ITypeMapper typeMapper)
        {
            if (expenseProjectRepository == null)
            {
                throw new ArgumentNullException(MethodBase.GetCurrentMethod().GetParameters()[0].Name);
            }

            if (typeMapper == null)
            {
                throw new ArgumentNullException(MethodBase.GetCurrentMethod().GetParameters()[1].Name);
            }

            this.expenseProjectRepository = expenseProjectRepository;
            this.typeMapper = typeMapper;
            this.logger = LoggerFactory.CreateLog();
        }

        #endregion

        #region IFinanceAppService Memebers

        /// <summary>
        /// <see cref="Tinter.Application.Modules.Finance.Services.IFinanceAppService"/>
        /// </summary>
        /// <param name="dtoMyExpenseProject"><see cref="Tinter.Application.Modules.Finance.Services.IFinanceAppService"/></param>
        /// <returns><see cref="Tinter.Application.Modules.Finance.Services.IFinanceAppService"/></returns>
        public MyExpenseProjectDto AddExpenseProject(MyExpenseProjectDto dtoMyExpenseProject)
        {
            if (dtoMyExpenseProject == null)
            {
                this.logger.Warning(Messages.WarrningCannotAddEmptyExpenseProject);

                return null;
            }

            var project = new MyExpenseProject
                              {
                ProjectName = dtoMyExpenseProject.ProjectName,
                ProjectDescription = dtoMyExpenseProject.ProjectDescription,
                ProjectId = dtoMyExpenseProject.ProjectId,
                UserId = dtoMyExpenseProject.UserId
            };

            this.expenseProjectRepository.Add(project);
            this.expenseProjectRepository.DataContext.Commit();

            return this.typeMapper.Map<MyExpenseProject, MyExpenseProjectDto>(project);
        }

        /// <summary>
        /// <see cref="Tinter.Application.Modules.Finance.Services.IFinanceAppService"/>
        /// </summary>
        /// <param name="myExpenseDto"><see cref="Tinter.Application.Modules.Finance.Services.IFinanceAppService"/></param>
        /// <returns><see cref="Tinter.Application.Modules.Finance.Services.IFinanceAppService"/></returns>
        public MyExpenseDto AddExpense(MyExpenseDto myExpenseDto)
        {
            if (myExpenseDto == null)
            {
                this.logger.Warning(Messages.WarrningCannotAddEmptyExpense);

                return null;
            }

            var expense = new MyExpense
                              {
                Amount = myExpenseDto.Amount,
                ExpenseDescription = myExpenseDto.ExpenseDescription,
                ExpenseName = myExpenseDto.ExpenseName,
                LogTimeUtc = myExpenseDto.LogTimeUtc,
                ProjectId = myExpenseDto.ProjectId,
                UserId = myExpenseDto.UserId
            };

            this.SaveExpense(expense);

            return this.typeMapper.Map<MyExpense, MyExpenseDto>(expense);
        }

        /// <summary>
        /// <see cref="Tinter.Application.Modules.Finance.Services.IFinanceAppService"/>
        /// </summary>
        /// <param name="dtoMyExpense"><see cref="Tinter.Application.Modules.Finance.Services.IFinanceAppService"/></param>
        public void UpdateExpense(MyExpenseDto dtoMyExpense)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// <see cref="Tinter.Application.Modules.Finance.Services.IFinanceAppService"/>
        /// </summary>
        /// <param name="expenseId"><see cref="Tinter.Application.Modules.Finance.Services.IFinanceAppService"/></param>
        public void RemoveExpense(int expenseId)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// <see cref="Tinter.Application.Modules.Finance.Services.IFinanceAppService"/>
        /// </summary>
        /// <param name="expenseId"><see cref="Tinter.Application.Modules.Finance.Services.IFinanceAppService"/></param>
        /// <returns><see cref="Tinter.Application.Modules.Finance.Services.IFinanceAppService"/></returns>
        public MyExpenseDto GetExpense(int expenseId)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// <see cref="Tinter.Application.Modules.Finance.Services.IFinanceAppService"/>
        /// </summary>
        /// <param name="userId"><see cref="Tinter.Application.Modules.Finance.Services.IFinanceAppService"/></param>
        /// <param name="pageNumber"><see cref="Tinter.Application.Modules.Finance.Services.IFinanceAppService"/></param>
        /// <param name="pageSize"><see cref="Tinter.Application.Modules.Finance.Services.IFinanceAppService"/></param>
        /// <returns><see cref="Tinter.Application.Modules.Finance.Services.IFinanceAppService"/></returns>
        public PagedItems<MyExpenseProjectDto> GetExpenseProjects(Guid userId, int pageNumber, int pageSize)
        {
            return this.expenseProjectRepository
                .GetFiltered(e => e.UserId == userId)
                .ToPagedList(pageNumber, pageSize)
                .MapTo<MyExpenseProjectDto>(this.typeMapper);
        }

        /// <summary>
        /// <see cref="Tinter.Application.Modules.Finance.Services.IFinanceAppService"/>
        /// </summary>
        /// <param name="userId"><see cref="Tinter.Application.Modules.Finance.Services.IFinanceAppService"/></param>
        /// <param name="fromUtc"><see cref="Tinter.Application.Modules.Finance.Services.IFinanceAppService"/></param>
        /// <param name="toUtc"><see cref="Tinter.Application.Modules.Finance.Services.IFinanceAppService"/></param>
        /// <param name="pageIndex"><see cref="Tinter.Application.Modules.Finance.Services.IFinanceAppService"/></param>
        /// <param name="pageSize"><see cref="Tinter.Application.Modules.Finance.Services.IFinanceAppService"/></param>
        /// <returns><see cref="Tinter.Application.Modules.Finance.Services.IFinanceAppService"/></returns>
        public PagedItems<MyExpenseDto> GetExpensesByRange(Guid userId, DateTime fromUtc, DateTime toUtc, int pageIndex, int pageSize)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// <see cref="Tinter.Application.Modules.Finance.Services.IFinanceAppService"/>
        /// </summary>
        /// <param name="projectId"><see cref="Tinter.Application.Modules.Finance.Services.IFinanceAppService"/></param>
        /// <param name="pageIndex"><see cref="Tinter.Application.Modules.Finance.Services.IFinanceAppService"/></param>
        /// <param name="pageSize"><see cref="Tinter.Application.Modules.Finance.Services.IFinanceAppService"/></param>
        /// <returns><see cref="Tinter.Application.Modules.Finance.Services.IFinanceAppService"/></returns>
        public PagedItems<MyExpenseDto> GetExpensesByProject(Guid projectId, int pageIndex, int pageSize)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// <see cref="Tinter.Application.Modules.Finance.Services.IFinanceAppService"/>
        /// </summary>
        /// <param name="userId"><see cref="Tinter.Application.Modules.Finance.Services.IFinanceAppService"/></param>
        /// <param name="text"><see cref="Tinter.Application.Modules.Finance.Services.IFinanceAppService"/></param>
        /// <param name="pageIndex"><see cref="Tinter.Application.Modules.Finance.Services.IFinanceAppService"/></param>
        /// <param name="pageSize"><see cref="Tinter.Application.Modules.Finance.Services.IFinanceAppService"/></param>
        /// <returns><see cref="Tinter.Application.Modules.Finance.Services.IFinanceAppService"/></returns>
        public PagedItems<MyExpenseDto> FindExpenses(Guid userId, string text, int pageIndex, int pageSize)
        {
            throw new NotImplementedException();
        }

        #endregion

        #region Private Methods

        private void SaveExpense(MyExpense expense)
        {
            //TODO: validate.

            if (expense.ExpenseId <= 0)
            {
                this.expenseProjectRepository.Get(expense.ProjectId).MyExpenses.Add(expense);
            }

            this.expenseProjectRepository.DataContext.Commit();
        }

        #endregion
    }
}
